package com.feng.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feng.entity.*;
import com.feng.entity.vo.*;
import com.feng.exception.SystemException;
import com.feng.mapper.RoleMapper;
import com.feng.service.AccountRoleService;
import com.feng.service.MenuService;
import com.feng.service.RoleMenuService;
import com.feng.service.RoleService;
import com.feng.utils.Const;
import com.feng.utils.HttpCodeEnum;
import com.feng.utils.WebUtils;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * (Role)表服务实现类
 *
 * @author feng
 * @since 2024-10-18 13:55:15
 */
@Service()
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    final
    AccountRoleService accountRoleService;
    final
    MenuService menuService;
    final
    RoleMenuService roleMenuService;

    public RoleServiceImpl(RoleMenuService roleMenuService, MenuService menuService, AccountRoleService accountRoleService) {
        this.roleMenuService = roleMenuService;
        this.menuService = menuService;
        this.accountRoleService = accountRoleService;
    }

    @Override
    public RestResponse listPageAllRole(RoleListVo roleList) {
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        List<ListAllRoleVo> listAllRoleVo = new ArrayList<>();
        wrapper.like(StringUtils.hasText(roleList.getRoleName()), Role::getRoleName, roleList.getRoleName())
                .eq(Objects.nonNull(roleList.getStatus()), Role::getStatus, roleList.getStatus());
        Page page = new Page<>(roleList.getCurrent(), roleList.getSize());
        page(page, wrapper);
        List<Role> roles = page.getRecords();
        roles.stream().forEach(role -> {
            listAllRoleVo.add(new ListAllRoleVo(role.getId(), role.getRoleName(), role.getRoleKey(), role.getRoleSort(),
                    role.getStatus().toString(), role.getCreateTime()));
        });
        return RestResponse.success(new ArticleListPageVo(listAllRoleVo, page.getTotal()));
    }

    @Override
    public RestResponse listAllRole() {
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        List<ListAllRoleVo> listAllRoleVo = new ArrayList<>();
        wrapper.eq(Role::getStatus, Const.ZERO);
        List<Role> roles = list(wrapper);
        roles.stream().forEach(role -> {
            listAllRoleVo.add(new ListAllRoleVo(role.getId(), role.getRoleName(), role.getRoleKey(), role.getRoleSort(),
                    role.getStatus().toString(), role.getCreateTime()));
        });
        return RestResponse.success(listAllRoleVo);
    }

    @Override
    public RestResponse updateStatus(UpdateRoleStatusReqVo updateRoleStatus) {
        Role role = new Role();
        role.setId(updateRoleStatus.getId());
        role.setStatus(updateRoleStatus.getStatus());
        updateById(role);
        return RestResponse.success();
    }

    @Transactional
    @Override
    public RestResponse addRole(AddRoleVo addRole) {
        if(roleNameExist(addRole.getRoleName())) throw new SystemException(HttpCodeEnum.ROLE_NAME_EXIST);
        if(roleKeyExist(addRole.getRoleKey())) throw new SystemException(HttpCodeEnum.ROLE_KEY_EXIST);
        getBaseMapper().AUTO_INCREMENT();
        Role role = new Role();
        role.setRoleName(addRole.getRoleName()).setRoleKey(addRole.getRoleKey()).setRoleSort(addRole.getRoleSort())
                .setStatus(addRole.getStatus());
        save(role);
        if(!addRole.getMenuIds().isEmpty()){
            List<RoleMenu> menus = addRole.getMenuIds().stream().parallel()
                    .map(menuId -> new RoleMenu(role.getId(), menuId))
                    .collect(Collectors.toList());
            roleMenuService.saveBatch(menus);
        }
        return RestResponse.success();
    }

    @Override
    public RestResponse getRoleById(Long id) {
        Role role = getById(id);
        UpdateRoleVo updateRoleVo = new UpdateRoleVo(role.getId(), role.getRoleName(), role.getRoleKey(), role.getRoleSort(),
                role.getStatus(), null);
        if(role != null){
            LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RoleMenu::getRoleId, role.getId());
            List<RoleMenu> roleMenus = roleMenuService.list(wrapper);
            if(!roleMenus.isEmpty() && roleMenus.size() > 0){
                List<Long> menuIds = roleMenus.stream()
                        .map(roleMenu -> roleMenu.getMenuId())
                        .collect(Collectors.toList());
                List<Menu> menus = menuIds.stream().map(menuId -> menuService.getById(menuId))
                        .collect(Collectors.toList());
                List<MenuTreeVo> menuTrees = menus.stream()
                        .map(menu -> new MenuTreeVo(menu.getId(), menu.getMenuName(), menu.getParentId(), null))
                        .collect(Collectors.toList());
                updateRoleVo.setMenuTree(menuTrees);
            }
        }
        return RestResponse.success(updateRoleVo);
    }

    @Transactional
    @Override
    public RestResponse updateRole(AddRoleVo addRole) {
        Role role = new Role();
        role.setId(addRole.getId()).setRoleName(addRole.getRoleName()).setRoleKey(addRole.getRoleKey()).setRoleSort(addRole.getRoleSort())
                .setStatus(addRole.getStatus());
        updateById(role);
        LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleMenu::getRoleId, role.getId());
        roleMenuService.remove(wrapper);
        if(!addRole.getMenuIds().isEmpty()){
            List<RoleMenu> menus = addRole.getMenuIds().stream().parallel()
                    .map(menuId -> new RoleMenu(role.getId(), menuId))
                    .collect(Collectors.toList());
            roleMenuService.saveBatch(menus);
        }
        return RestResponse.success();
    }

    @Transactional
    @Override
    public RestResponse deleteRole(Long id) {
        removeById(id);
        LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleMenu::getRoleId, id);
        roleMenuService.remove(wrapper);
        LambdaQueryWrapper<AccountRole> arWrapper = new LambdaQueryWrapper<>();
        arWrapper.eq(AccountRole::getRoleId, id);
        accountRoleService.remove(arWrapper);
        return RestResponse.success();
    }

    @Transactional
    @Override
    public RestResponse delBatchRole(List<Long> ids) {
        removeBatchByIds(ids);
        ids.stream().forEach(id -> {
            LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RoleMenu::getRoleId, id);
            roleMenuService.remove(wrapper);
            LambdaQueryWrapper<AccountRole> arWrapper = new LambdaQueryWrapper<>();
            arWrapper.eq(AccountRole::getRoleId, id);
            accountRoleService.remove(arWrapper);
        });
        return RestResponse.success();
    }

    @Override
    public void export(HttpServletResponse response) {
        try (ServletOutputStream outputStream = response.getOutputStream()){
            WebUtils.excelResponseHeader(response, "角色");
            List<Role> roles = list();
            List<RoleExportVo> exportVo = new ArrayList<>();
            roles.forEach(role -> exportVo.add(new RoleExportVo(role.getId(), role.getRoleName(), role.getRoleKey(), role.getRoleSort(),
                    role.getStatus(), role.getCreateTime())));
            EasyExcel.write(outputStream, RoleExportVo.class)
                    .autoCloseStream(Boolean.FALSE).sheet("角色导出")
                    .doWrite(exportVo);
        } catch (Exception e) {
            response.reset();
            WebUtils.renderString(response, RestResponse.failure(500, "角色导出出现了错误").asJsonString());
        }
    }


    private boolean roleNameExist(String roleName) {
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Role::getRoleName, roleName);
        long count = count(wrapper);
        return count > 0;
    }
    private boolean roleKeyExist(String roleKey) {
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Role::getRoleKey, roleKey);
        long count = count(wrapper);
        return count > 0;
    }
}

